/***************************************************
File:           LPK_DispatchOnAnimatorCycle.cs
Authors:        Christopher Onorati
Last Updated:   6/10/2019
Last Version:   2018.3.14

Description:
  This component detects when an animator has finished a
  cycle for event dispatching.

This script is a basic and generic implementation of its 
functionality. It is designed for educational purposes and 
aimed at helping beginners.

Copyright 2018-2019, DigiPen Institute of Technology
***************************************************/

using UnityEngine;
using UnityEditor;

namespace LPK
{

/**
* CLASS NAME  : LPK_DispatchOnAnimatorCycle
* DESCRIPTION : Dispatcher for animator cycles.
**/
public class LPK_DispatchOnAnimatorCycle : LPK_Component
{
    /************************************************************************************/

    [Tooltip("Animator to detect cycles finishing on.")]
    [Rename("Animator")]
    public Animator m_cAnimator;

    [Header("Event Sending Info")]

    [Tooltip("Event sent when an animator has finished its cycle.")]
    public LPK_EventSendingInfo m_AnimatorCycleFinishedEvent;

    /************************************************************************************/

    //Used for nonlooping animations.
    bool m_bHasFired = false;

    /**
    * FUNCTION NAME: OnStart
    * DESCRIPTION  : Sets up which animator to listen for.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    override protected void OnStart()
    {
        if(m_cAnimator == null)
        {
            if (GetComponent<Animator>())
                m_cAnimator = GetComponent<Animator>();
        }

        if (m_bPrintDebug && m_cAnimator == null)
            LPK_PrintWarning(this, "No animator found to use for event dispatching.");
    }

    /**
    * FUNCTION NAME: Update
    * DESCRIPTION  : Checks state of animator being listend to.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    void Update()
    {
        if (m_cAnimator == null)
            return;

        if (m_cAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime < 1.0f)
            m_bHasFired = false;

        if (m_cAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1.0f && !m_bHasFired)
        {
            DispatchAnimationCycleEvent();

            if (m_cAnimator.GetCurrentAnimatorClipInfo(0)[0].clip.isLooping)
                m_cAnimator.PlayInFixedTime(0, -1, m_cAnimator.GetCurrentAnimatorStateInfo(0).normalizedTime - 1.0f);
            else
                m_bHasFired = true;
        }
    }

    /**
    * FUNCTION NAME: DispatchAnimationCycleEvent
    * DESCRIPTION  : Sends event out for a cycle of the animation being finished.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    void DispatchAnimationCycleEvent()
    {
        if(m_AnimatorCycleFinishedEvent != null && m_AnimatorCycleFinishedEvent.m_Event != null)
        {
            if(m_AnimatorCycleFinishedEvent.m_EventSendingMode == LPK_EventSendingInfo.LPK_EventSendingMode.ALL)
                m_AnimatorCycleFinishedEvent.m_Event.Dispatch(null);
            else if(m_AnimatorCycleFinishedEvent.m_EventSendingMode == LPK_EventSendingInfo.LPK_EventSendingMode.OWNER)
                m_AnimatorCycleFinishedEvent.m_Event.Dispatch(gameObject);

            if (m_bPrintDebug)
                LPK_PrintDebug(this, "Animator Cycle Finished event dispatched");
        }
    }
}

#if UNITY_EDITOR

[CustomEditor(typeof(LPK_DispatchOnAnimatorCycle))]
public class LPK_DispatchOnAnimatorCycleEditor : Editor
{
    SerializedProperty toggleType;
    SerializedProperty animator;

    SerializedProperty eventTriggers;

    SerializedProperty animatorCycleFinishedReceivers;

    /**
    * FUNCTION NAME: OnEnable
    * DESCRIPTION  : Save out serialized classes.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    void OnEnable()
    {
        toggleType = serializedObject.FindProperty("m_eToggleType");
        animator = serializedObject.FindProperty("m_cAnimator");

        eventTriggers = serializedObject.FindProperty("m_EventTrigger");

        animatorCycleFinishedReceivers = serializedObject.FindProperty("m_AnimatorCycleFinishedEvent");
    }

    /**
    * FUNCTION NAME: OnInspectorGUI
    * DESCRIPTION  : Override GUI for inspector.
    * INPUTS       : None
    * OUTPUTS      : None
    **/
    public override void OnInspectorGUI()
    {
        LPK_DispatchOnAnimatorCycle owner = (LPK_DispatchOnAnimatorCycle)target;

        LPK_DispatchOnAnimatorCycle editorOwner = owner.GetComponent<LPK_DispatchOnAnimatorCycle>();

        EditorGUI.BeginDisabledGroup(true);
        EditorGUILayout.BeginHorizontal();
        EditorGUILayout.PrefixLabel("Script");
        editorOwner = (LPK_DispatchOnAnimatorCycle)EditorGUILayout.ObjectField(editorOwner, typeof(LPK_DispatchOnAnimatorCycle), false);
        GUILayout.EndHorizontal();
        EditorGUI.EndDisabledGroup();

        //Undo saving.
        Undo.RecordObject(owner, "Property changes on LPK_DispatchOnAnimatorCycle");

        GUILayout.Space(10);
        EditorGUILayout.LabelField("Base Properties", EditorStyles.boldLabel);

        owner.m_bPrintDebug = EditorGUILayout.Toggle(new GUIContent("Print Debug Info", "Toggle console debug messages."), owner.m_bPrintDebug);

        GUILayout.Space(10);
        EditorGUILayout.LabelField("Component Properties", EditorStyles.boldLabel);

        EditorGUILayout.PropertyField(animator, true);

        //Events
        EditorGUILayout.PropertyField(animatorCycleFinishedReceivers, true);

        //Apply changes.
        serializedObject.ApplyModifiedProperties();
    }
}

#endif  //UNITY_EDITOR

}   //LPK
